Skip to content

Conversation

@stoneTiger0912
Copy link
Member

@stoneTiger0912 stoneTiger0912 commented Aug 20, 2025

📝 변경 내용


✅ 체크리스트

  • 코드가 정상적으로 동작함
  • 테스트 코드 통과함
  • 문서(README 등)를 최신화함
  • 코드 스타일 가이드 준수

💬 기타 참고 사항

Summary by CodeRabbit

  • New Features

    • 그룹 삭제 엔드포인트 추가: 그룹 오너만, 다른 구성원이 없을 때 삭제 가능하며 성공 시 204 반환.
  • Refactor

    • 오류 코드셋 재정비 및 상태코드(권한 403, 충돌 409) 정리 및 메시지 필드 추가.
    • 삭제 타임스탬프를 DB 현재시각으로 처리하도록 변경.
    • 그룹 구성원 연관관계에 지연로딩 적용 및 소프트삭제 동작 제거.
  • Tests

    • 그룹 삭제 성공 및 실패(오너 아님, 다른 구성원 존재) 단위 테스트 3건 추가.
  • Chores

    • 그룹 내 특정 사용자 제외 멤버 수 계산 메서드 추가.

@stoneTiger0912 stoneTiger0912 self-assigned this Aug 20, 2025
@coderabbitai
Copy link

coderabbitai bot commented Aug 20, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

그룹 삭제 API와 서비스 로직이 추가되고, Group 엔티티의 soft-delete SQL이 DB CURRENT_TIMESTAMP로 변경되었으며, GroupMember의 클래스레벨 soft-delete가 제거되고 연관관계가 LAZY로 전환되었습니다. 리포지토리에 파생 쿼리와 에러 코드 재정렬, 서비스 단위 테스트 3건이 추가되었습니다.

Changes

Cohort / File(s) Summary
API Controller
src/main/java/project/flipnote/group/controller/GroupController.java
DELETE /v1/groups/{groupId} 엔드포인트 추가: 인증 주체로 그룹 삭제를 서비스에 위임하고 성공 시 204 No Content 반환.
Service Layer
src/main/java/project/flipnote/group/service/GroupService.java
validateGroupInUser 반환형이 booleanGroupMember로 변경; deleteGroup(AuthPrinciple, Long) 메서드(@transactional) 추가: 오너 권한 검증, 다른 유저 존재 검사 후 삭제 수행; 보조 헬퍼 메서드 추가.
Entities
src/main/java/project/flipnote/group/entity/Group.java, src/main/java/project/flipnote/group/entity/GroupMember.java
Group: @SQLDeletenow()CURRENT_TIMESTAMP 변경. GroupMember: 클래스레벨 soft-delete 어노테이션 제거(soft-delete 비활성화), @ManyToOne(fetch = FetchType.LAZY)로 연관관계 LAZY 전환.
Repository
src/main/java/project/flipnote/group/repository/GroupMemberRepository.java
파생 쿼리 long countByGroup_idAndUser_idNot(Long groupId, Long userId) 추가.
Error Codes
src/main/java/project/flipnote/group/exception/GroupErrorCode.java
에러 코드 재정렬 및 신규 항목 추가(USER_NOT_PERMISSION, OTHER_USER_EXIST_IN_GROUP 등), message 필드 추가 및 생성자(필드) 업데이트.
Tests
src/test/java/project/flipnote/group/service/GroupServiceTest.java
그룹 삭제 성공/실패(오너 아님, 다른 유저 존재) 케이스 3건 추가 및 관련 리포지토리 목 설정/검증 추가.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client as 클라이언트
  participant Controller as GroupController
  participant Service as GroupService
  participant GroupRepo as GroupRepository
  participant MemberRepo as GroupMemberRepository
  participant Auth as AuthPrinciple

  Client->>Controller: DELETE /v1/groups/{groupId}
  Controller->>Service: deleteGroup(Auth, groupId)
  Service->>GroupRepo: findByIdAndDeletedAtIsNull(groupId)
  Service->>Auth: getUserProfile()
  Service->>MemberRepo: findByGroup_IdAndUser_Id(groupId, userId)
  alt 멤버 아님
    Service-->>Controller: throw USER_NOT_IN_GROUP
  else 멤버임
    alt 오너 아님
      Service-->>Controller: throw USER_NOT_PERMISSION
    else 오너임
      Service->>MemberRepo: countByGroup_idAndUser_idNot(groupId, ownerId)
      alt 다른 유저 존재
        Service-->>Controller: throw OTHER_USER_EXIST_IN_GROUP
      else 유일 멤버
        Service->>GroupRepo: delete(group)
        note right of GroupRepo #dbeafe: @SQLDelete -> UPDATE ... SET deleted_at=CURRENT_TIMESTAMP
        Service-->>Controller: 완료
      end
    end
  end
  Controller-->>Client: 204 No Content
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • dungbik

Poem

(_/)
(•ᴥ•) 나는 토끼, 코드밭을 폴짝
오너인지 묻고, 다른 발자국 세어보았네
모두 비어있으면 삭제 찰칵—깡충깡충
당근 들고 축하해요, 변경 완료 🐇✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 60bb01a and 3c2fa90.

📒 Files selected for processing (2)
  • src/main/java/project/flipnote/group/repository/GroupMemberRepository.java (1 hunks)
  • src/main/java/project/flipnote/group/service/GroupService.java (4 hunks)
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/delete-group

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (7)
src/main/java/project/flipnote/group/exception/GroupErrorCode.java (1)

17-17: 오탈자(띄어쓰기) 수정 제안

메시지의 자연스러운 띄어쓰기를 위해 아래처럼 수정하는 것을 제안드립니다.

- OTHER_USER_EXIST_IN_GROUP(HttpStatus.CONFLICT, "GROUP_005", "그룹내 오너 제외 유저가 존재합니다.");
+ OTHER_USER_EXIST_IN_GROUP(HttpStatus.CONFLICT, "GROUP_005", "그룹 내 오너 제외 유저가 존재합니다.");
src/main/java/project/flipnote/group/repository/GroupMemberRepository.java (1)

23-24: 존재 여부는 count 대신 exists 파생 쿼리 사용 권장 + 네이밍 일관성

현재 사용 목적이 "다른 멤버가 존재하는지만" 확인이라면 count보다 exists 파생 쿼리가 의도에 더 맞고 일반적으로 더 가볍습니다. 또한 같은 인터페이스 안에서 findByGroup_IdAndUser_Id와 케이스가 섞여 있으므로, 새 메서드는 CamelCase 경로 표기(Group_Id, User_Id)로 맞추는 것을 권장합니다.

제안 diff:

- long countByGroup_idAndUser_idNot(Long groupId, Long userId);
+ boolean existsByGroup_IdAndUser_IdNot(Long groupId, Long userId);

서비스 측 호출 예시(범위 밖 참고용):

// GroupService
if (groupMemberRepository.existsByGroup_IdAndUser_IdNot(groupId, ownerUserId)) {
    throw new BizException(GroupErrorCode.OTHER_USER_EXIST_IN_GROUP);
}

추가로, 삭제된 그룹을 전역적으로 배제해야 한다면(soft-delete 유지), 파생 쿼리만으로는 Group.deletedAt 조건이 자동 적용되지 않습니다. 그런 요구가 있다면 커스텀 @query로 그룹 조인 후 조건을 명시하는 방식을 고려해주세요:

@Query("""
select (count(gm) > 0) 
from GroupMember gm 
join gm.group g 
where g.id = :groupId 
  and g.deletedAt is null 
  and gm.user.id <> :userId
""")
boolean existsActiveMemberOtherThanUser(@Param("groupId") Long groupId, @Param("userId") Long userId);
src/main/java/project/flipnote/group/controller/GroupController.java (1)

47-56: 다른 DELETE 엔드포인트와의 상태 코드 일관성 제안

동일 프로젝트 내 일부 DELETE는 200 OK를 반환합니다(예: GroupInvitationController). 가능하다면 모두 204로 통일해 클라이언트 처리를 단순화하는 것을 권장합니다.

참고(범위 밖 예시):

// GroupInvitationController
// return ResponseEntity.ok().build();
return ResponseEntity.noContent().build();
src/test/java/project/flipnote/group/service/GroupServiceTest.java (2)

241-269: 오너가 아닌 경우: 사용자 조회 스텁 ID 정렬 및 삭제 호출 방지 검증을 권장합니다

Line 260의 1L 하드코딩은 성공 케이스와 동일한 불안정성을 유발할 수 있습니다. 또한 음수 경로에서 delete 호출이 전혀 발생하지 않았음을 검증하면 회귀를 방지할 수 있습니다.

아래 변경을 제안합니다:

-		given(userProfileRepository.findByIdAndStatus(1L, UserStatus.ACTIVE)).willReturn(Optional.ofNullable(userProfile));
+		given(userProfileRepository.findByIdAndStatus(userProfile.getId(), UserStatus.ACTIVE)).willReturn(Optional.ofNullable(userProfile));
@@
 		BizException exception =
 			assertThrows(BizException.class, () -> groupService.deleteGroup(authPrinciple, 1L));
 
 		assertEquals(GroupErrorCode.USER_NOT_PERMISSION, exception.getErrorCode());
+		// 삭제 호출이 일어나지 않았는지 검증
+		then(groupRepository).should(never()).delete(any());

추가로 필요한 정적 임포트(테스트 파일 최상단, 기존 BDDMockito 스타일과 호환됩니다):

import static org.mockito.Mockito.*;

272-300: 그룹에 다른 유저가 존재하는 경우: 사용자 조회 스텁 ID 정렬 및 삭제 호출 방지 검증 추가

성공/다른 실패 케이스와 동일한 보완 사항입니다.

아래 변경을 제안합니다:

-		given(userProfileRepository.findByIdAndStatus(1L, UserStatus.ACTIVE)).willReturn(Optional.ofNullable(userProfile));
+		given(userProfileRepository.findByIdAndStatus(userProfile.getId(), UserStatus.ACTIVE)).willReturn(Optional.ofNullable(userProfile));
@@
 		BizException exception =
 			assertThrows(BizException.class, () -> groupService.deleteGroup(authPrinciple, 1L));
 
 		assertEquals(GroupErrorCode.OTHER_USER_EXIST_IN_GROUP, exception.getErrorCode());
+		// 삭제 호출이 일어나지 않았는지 검증
+		then(groupRepository).should(never()).delete(any());

필요 시 상단에 다음 임포트를 추가하세요:

import static org.mockito.Mockito.*;
src/main/java/project/flipnote/group/service/GroupService.java (2)

167-176: 불리언 로직 단순화 및 메서드 명시성 향상 권장

현재 메서드명(checkUserNotExistInGroup)과 반환 의미가 직관적이지 않고, 불리언 로직도 단순화 가능합니다. “오너만 존재하는지”를 나타내는 이름과 형태로 바꾸면 가독성이 좋아집니다.

아래처럼 단순화 및 명확화하고, 호출부도 함께 수정하는 것을 제안합니다.

-	private boolean checkUserNotExistInGroup(UserProfile user, Long groupId) {
-		long count = groupMemberRepository.countByGroup_idAndUser_idNot(groupId, user.getId());
-		if (count > 0) {
-			return false;
-		}
-		return true;
-	}
+	private boolean isOnlyMemberInGroup(Long groupId, Long userId) {
+		return groupMemberRepository.countByGroup_idAndUser_idNot(groupId, userId) == 0;
+	}

호출부 변경(아래 삭제 메서드 코멘트에 함께 제시):


195-219: 동시성 제어 부재: 멤버 추가/삭제와의 경쟁 상태 가능성

동일 PR 내 GroupMemberPolicyService.addGroupMember가 Redisson 분산락을 사용해 멤버 추가를 직렬화하는 반면, deleteGroup은 어떤 락도 사용하지 않습니다. 현재 로직은 “다른 멤버 없음”을 count 쿼리로 확인한 뒤 삭제합니다. 멤버 추가 작업과 경합 시 다음 이슈가 발생할 수 있습니다:

  • 삭제 시점 count=0 → 직후 멤버 추가(잠금으로 보호) → 삭제 커밋 순으로 실행될 경우, 정책상 허용되지 않는 타이밍 이슈가 생길 수 있음.

최소한 동일한 락 키(예: "group_member_lock:{groupId}")로 삭제도 보호하거나, DB 레벨에서 그룹 행에 대한 SELECT ... FOR UPDATE 기반 조회로 일관성을 확보하는 것을 권장합니다.

락 적용 예시(의존성 주입 및 예외 처리 생략):

// 1) Redisson 락 적용
String lockKey = "group_member_lock:" + groupId;
RLock lock = redissonClient.getLock(lockKey);
boolean isLocked = lock.tryLock(2, 3, TimeUnit.SECONDS);
try {
    if (!isLocked) throw new BizException(CommonErrorCode.SERVICE_TEMPORARILY_UNAVAILABLE);

    Group lockedGroup = groupRepository.findByIdForUpdate(groupId)
        .orElseThrow(() -> new BizException(GroupErrorCode.GROUP_NOT_FOUND));

    // 이후 validateUser, validateGroupInUser, 오너 검증, isOnlyMemberInGroup(groupId, user.getId()) 재검증
    // 마지막에 delete
} finally {
    if (isLocked && lock.isHeldByCurrentThread()) lock.unlock();
}

또한, Enum 비교는 동등성(equals) 대신 동치(==) 비교가 관용적입니다. 작은 개선을 제안합니다.

-		if (!groupMember.getRole().equals(GroupMemberRole.OWNER)) {
+		if (groupMember.getRole() != GroupMemberRole.OWNER) {
 			throw new BizException(GroupErrorCode.USER_NOT_PERMISSION);
 		}

위의 메서드 명/호출부 개선을 함께 반영하려면:

-		if (!checkUserNotExistInGroup(user, groupId)) {
+		if (!isOnlyMemberInGroup(groupId, user.getId())) {
 			throw new BizException(GroupErrorCode.OTHER_USER_EXIST_IN_GROUP);
 		}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a9676b8 and f3105cd.

📒 Files selected for processing (7)
  • src/main/java/project/flipnote/group/controller/GroupController.java (4 hunks)
  • src/main/java/project/flipnote/group/entity/Group.java (1 hunks)
  • src/main/java/project/flipnote/group/entity/GroupMember.java (2 hunks)
  • src/main/java/project/flipnote/group/exception/GroupErrorCode.java (1 hunks)
  • src/main/java/project/flipnote/group/repository/GroupMemberRepository.java (1 hunks)
  • src/main/java/project/flipnote/group/service/GroupService.java (4 hunks)
  • src/test/java/project/flipnote/group/service/GroupServiceTest.java (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (7)
src/main/java/project/flipnote/group/entity/Group.java (3)
src/main/java/project/flipnote/common/entity/SoftDeletableEntity.java (3)
  • softDelete (14-16)
  • SoftDeletableEntity (8-21)
  • isDeleted (18-20)
src/main/java/project/flipnote/cardset/entity/CardSet.java (1)
  • deleteCardSet (68-70)
src/main/java/project/flipnote/common/entity/BaseEntity.java (1)
  • BaseEntity (14-26)
src/main/java/project/flipnote/group/repository/GroupMemberRepository.java (2)
src/main/java/project/flipnote/groupjoin/repository/GroupJoinRepository.java (1)
  • Repository (12-19)
src/main/java/project/flipnote/group/repository/GroupInvitationRepository.java (2)
  • GroupInvitationRepository (17-41)
  • findAllByInviteeUserId (29-29)
src/main/java/project/flipnote/group/controller/GroupController.java (2)
src/main/java/project/flipnote/group/controller/GroupInvitationController.java (1)
  • DeleteMapping (41-50)
src/main/java/project/flipnote/groupjoin/controller/GroupJoinController.java (1)
  • DeleteMapping (56-65)
src/main/java/project/flipnote/group/entity/GroupMember.java (2)
src/main/java/project/flipnote/groupjoin/entity/GroupJoin.java (1)
  • Getter (22-65)
src/main/java/project/flipnote/group/entity/GroupRolePermission.java (1)
  • Entity (11-41)
src/test/java/project/flipnote/group/service/GroupServiceTest.java (2)
src/test/java/project/flipnote/groupjoin/service/GroupJoinServiceTest.java (1)
  • ExtendWith (35-283)
src/test/java/project/flipnote/cardset/service/CardSetServiceTest.java (1)
  • ExtendWith (39-123)
src/main/java/project/flipnote/group/exception/GroupErrorCode.java (5)
src/main/java/project/flipnote/groupjoin/exception/GroupJoinErrorCode.java (2)
  • Getter (8-26)
  • Override (22-25)
src/main/java/project/flipnote/group/exception/GroupInvitationErrorCode.java (2)
  • Getter (9-25)
  • Override (21-24)
src/main/java/project/flipnote/cardset/exception/CardSetErrorCode.java (1)
  • Getter (9-23)
src/main/java/project/flipnote/user/exception/UserErrorCode.java (1)
  • Getter (9-24)
src/main/java/project/flipnote/groupjoin/service/GroupJoinService.java (1)
  • GroupErrorCode (52-52)
src/main/java/project/flipnote/group/service/GroupService.java (2)
src/main/java/project/flipnote/group/service/GroupMemberPolicyService.java (1)
  • RequiredArgsConstructor (22-69)
src/main/java/project/flipnote/groupjoin/service/GroupJoinService.java (1)
  • Slf4j (30-244)
🔇 Additional comments (10)
src/main/java/project/flipnote/group/entity/GroupMember.java (1)

33-39: LAZY 전환 LGTM

@manytoone 연관관계를 LAZY로 명시한 변경은 적절합니다. 불필요한 즉시 로딩을 방지해 쿼리 수와 메모리 사용을 줄일 수 있습니다.

src/main/java/project/flipnote/group/exception/GroupErrorCode.java (2)

13-17: 에러 코드 개편 방향 타당

그룹 삭제/권한 검증 시나리오를 반영한 코드 추가와 재정렬의 의도가 명확합니다. 서비스/컨트롤러 계층에서의 사용과도 일관됩니다.


13-17: 하드코딩된 에러 코드 문자열 영향 범위 확인 완료

저장소 전반에서 GROUP_001~GROUP_005 문자열은 enum 정의(src/main/java/project/flipnote/group/exception/GroupErrorCode.java) 외에 사용처가 발견되지 않았습니다.
별도 문서·테스트·대시보드의 하드코딩된 참조는 없으므로 추가 업데이트가 필요 없음을 확인했습니다.

src/main/java/project/flipnote/group/entity/Group.java (2)

32-32: DB 독립성 향상 LGTM

now() → CURRENT_TIMESTAMP로의 전환은 표준 SQL 함수 사용 측면에서 바람직합니다. 주요 RDBMS 및 테스트용 H2와의 호환성도 일반적으로 원활합니다.


32-32: DB 방언별 동작 확인 요청(테스트/운영 일치성)

테스트(H2 등)와 운영(MySQL/PostgreSQL 등) DB 방언이 다른 경우, @SQLDelete가 생성하는 SQL이 동일하게 동작하는지 한 번만 확인 부탁드립니다. 특히 트리거/타임존 처리 정책 차이가 있다면 감사 로깅 시각과의 정합성도 점검해주세요.

src/main/java/project/flipnote/group/controller/GroupController.java (1)

47-56: DELETE 엔드포인트 204 No Content 반환 적절

권한/검증 로직을 서비스에 위임하고, 성공 시 204를 반환하는 형태는 REST 관점에서 명확합니다. 엔드포인트/시그니처도 일관적입니다.

src/test/java/project/flipnote/group/service/GroupServiceTest.java (1)

28-29: 삭제 테스트를 위한 엔티티 임포트 추가, 적절합니다

새로운 삭제 시나리오에서 GroupMember/GroupMemberRole 사용을 위해 필요한 임포트입니다.

src/main/java/project/flipnote/group/service/GroupService.java (3)

57-61: 멤버십 검증의 에러 코드 일관성 유지 필요

이 메서드는 GroupJoinErrorCode.USER_NOT_IN_GROUP을 사용합니다. 클래스 내 다른 멤버십 검증(예: hasPermission)에서도 동일 에러 코드를 사용하도록 정렬하는 것을 권장합니다. 혼재 시 핸들러/문서화 일관성이 깨질 수 있습니다.

클래스 내 다른 위치도 아래와 같이 통일해 주세요(선택적 예시, 변경은 본 범위 밖):

// outside current hunk: hasPermission(...)
GroupMember groupMember = groupMemberRepository.findByGroup_IdAndUser_Id(groupId, userId).orElseThrow(
    () -> new BizException(GroupJoinErrorCode.USER_NOT_IN_GROUP)
);

142-142: LGTM: 저장 즉시 반환으로 간결화

createGroup에서 save 후 즉시 반환하는 구조가 명확합니다.


190-190: 멤버십 검증 재사용으로 중복 제거, 적절합니다

findGroupDetail에서 validateGroupInUser를 재사용하여 존재 여부 검증을 일원화한 점이 좋습니다.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/main/java/project/flipnote/group/exception/GroupErrorCode.java (3)

16-16: 한글 문구 띄어쓰기/조사 보정

문구의 자연스러움과 가독성을 위해 다음과 같이 수정 제안합니다.

-	OTHER_USER_EXIST_IN_GROUP(HttpStatus.CONFLICT, "GROUP_007", "그룹내 오너 제외 유저가 존재합니다."),
+	OTHER_USER_EXIST_IN_GROUP(HttpStatus.CONFLICT, "GROUP_007", "그룹 내 오너를 제외한 유저가 존재합니다."),

15-15: 용어 일관성(유저/회원/멤버) 정리 제안

동일 enum 내에서 ‘유저’(Line 15)와 ‘회원’(Line 18)이 혼용됩니다. 코드 전반에서는 ‘멤버/사용자’도 함께 쓰이고 있어 혼재가 커 보입니다. 이번 PR에서 강제 변경까지는 아니지만, 이 파일만큼은 통일하는 것을 권장합니다. 아래 예시는 ‘멤버’ 기준입니다.

-	USER_NOT_IN_GROUP(HttpStatus.NOT_FOUND, "GROUP_003", "그룹에 유저가 존재하지 않습니다."),
+	USER_NOT_IN_GROUP(HttpStatus.NOT_FOUND, "GROUP_003", "해당 사용자는 그룹의 멤버가 아닙니다."),
...
-	ALREADY_GROUP_MEMBER(HttpStatus.CONFLICT, "GROUP_005", "이미 그룹 회원입니다.");
+	ALREADY_GROUP_MEMBER(HttpStatus.CONFLICT, "GROUP_005", "이미 그룹 멤버입니다.");

팀에서 합의된 용어집(사용자/유저/멤버/회원 중 택1)이 있다면 그 기준에 맞춰 일괄 정리하는 이슈로 분리하는 것도 좋습니다.

Also applies to: 18-18


13-13: 하드코딩된 최대 인원(100) → 상수/설정 연동으로 드리프트 방지

비즈니스 정책 값(100)이 메시지에 하드코딩되어 있어, 정책 변경 시 메시지와 실제 검증 로직이 어긋날 수 있습니다. 공용 상수나 설정값을 참조하도록 변경을 권장합니다.

  • 예시: 정책 상수 도입
// project/flipnote/group/GroupPolicy.java
public final class GroupPolicy {
    private GroupPolicy() {}
    public static final int MAX_MEMBERS = 100;
}
  • 해당 enum 항목 수정
-	INVALID_MAX_MEMBER(HttpStatus.BAD_REQUEST, "GROUP_001", "최대 인원 수는 1 이상 100 이하여야 합니다."),
+	INVALID_MAX_MEMBER(HttpStatus.BAD_REQUEST, "GROUP_001",
+		"최대 인원 수는 1 이상 " + GroupPolicy.MAX_MEMBERS + " 이하여야 합니다."),

또는 메시지를 일반화하여 숫자를 배제하는 방법도 있습니다(“최대 인원 수가 허용 범위를 초과했습니다.”).

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between bffc577 and c06e4cc.

📒 Files selected for processing (3)
  • src/main/java/project/flipnote/group/entity/Group.java (1 hunks)
  • src/main/java/project/flipnote/group/exception/GroupErrorCode.java (1 hunks)
  • src/main/java/project/flipnote/group/service/GroupService.java (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/project/flipnote/group/entity/Group.java
  • src/main/java/project/flipnote/group/service/GroupService.java
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/java/project/flipnote/group/exception/GroupErrorCode.java (6)
src/main/java/project/flipnote/group/exception/GroupInvitationErrorCode.java (2)
  • Getter (9-25)
  • Override (21-24)
src/main/java/project/flipnote/groupjoin/exception/GroupJoinErrorCode.java (2)
  • Getter (8-26)
  • Override (22-25)
src/main/java/project/flipnote/cardset/exception/CardSetErrorCode.java (1)
  • Getter (9-23)
src/main/java/project/flipnote/common/exception/CommonErrorCode.java (1)
  • Getter (8-18)
src/main/java/project/flipnote/user/exception/UserErrorCode.java (1)
  • Getter (9-24)
src/main/java/project/flipnote/notification/exception/NotificationErrorCode.java (1)
  • Getter (9-23)
🔇 Additional comments (1)
src/main/java/project/flipnote/group/exception/GroupErrorCode.java (1)

12-18: 검증 결과: GroupErrorCode 변경 영향도는 내부 enum 상수 참조에만 국한됩니다.

  • 원시 문자열 "GROUP_XXX" 형태로 직접 사용된 곳은 GroupErrorCode.java 파일 내부뿐이며, 레포 전체에 걸쳐 다른 파일에서 하드코딩된 문자열 참조는 없습니다.
  • 서비스 로직 및 테스트 코드에서는 모두 GroupErrorCode.상수명 형태로 enum 상수를 참조하고 있어, enum 멤버명 또는 리터럴 문자열을 변경할 경우 컴파일 에러 및 테스트 실패가 발생합니다.
  • 테스트 코드(GroupServiceTest, GroupJoinServiceTest 등)에서도 enum 상수 비교를 통해 에러 코드를 검증하므로, 상수명 변경 시 일괄 수정이 필요합니다.
  • 문서나 README, OpenAPI 스펙 등 외부 표시용 레퍼런스는 발견되지 않았으나, API 클라이언트가 문자열 코드를 사용 중일 가능성이 있으므로, 변경 시 별도 공지 및 문서 업데이트를 고려해 주세요.

위 결과를 토대로, 현 상태 유지 시 추가 조치는 불필요하며, enum 상수명·리터럴 변경 시에는 호출부 및 테스트 일괄 수정이 필요함을 안내드립니다.

@stoneTiger0912 stoneTiger0912 requested a review from dungbik August 21, 2025 05:29
@stoneTiger0912 stoneTiger0912 added the enhancement New feature or request label Aug 21, 2025
Copy link
Contributor

@dungbik dungbik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@stoneTiger0912 stoneTiger0912 merged commit 25bf154 into develop Aug 21, 2025
2 of 3 checks passed
@stoneTiger0912 stoneTiger0912 deleted the feat/delete-group branch August 21, 2025 07:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants